/*
 * $Id: sysapi_poll.c,v 1.17 2005-12-28 10:39:42 bacon Exp $
 */

#include <xp/bas/sysapi.h>
#include <xp/bas/errno.h>

#if defined(__linux)
	#include <linux/unistd.h>
	static inline _syscall3 (int, poll,
		xp_poll_t*, ufds, unsigned int, nfds, int, timeout);
#elif !defined(XP_HAVE_POLL_H)
	#ifndef _WIN32
		#include <sys/select.h>
	#endif
#endif

#ifndef XP_HAVE_POLL_H
static xp_ssize_t __emul_poll_with_select (
	xp_poll_t* fds, xp_size_t nfds, xp_time_t timeout);
#endif

xp_ssize_t xp_poll (xp_poll_t* fds, xp_size_t nfds, xp_time_t timeout)
{
#if defined(__linux) || defined(XP_HAVE_POLL_H)
	return poll (fds, nfds, (int)timeout);
#else
	return __emul_poll_with_select (fds, nfds, timeout);
#endif
}

#ifndef XP_HAVE_POLL_H
static xp_ssize_t __emul_poll_with_select (
	xp_poll_t* fds, xp_size_t nfds, xp_time_t timeout)
{
	xp_timeval_t tv;
	xp_poll_t* p, * end;
	xp_ssize_t n;
	int max = -1;
	fd_set readset, writeset, exceptset;

	XP_TIME_TO_TIMEVAL (timeout, &tv);
	if (nfds == 0) {
		n = (xp_ssize_t)select (0, 
			XP_NULL, XP_NULL, XP_NULL, ((timeout >= 0)? &tv: XP_NULL));
#ifndef _WIN32
		if (n == -1) {
			xp_seterrno (xp_maperrno_wsa(WSAGetLastError()));
		}
#endif
		return n;
	}
	
	FD_ZERO (&readset);
	FD_ZERO (&writeset);
	FD_ZERO (&exceptset);

	end = fds + nfds;
	for (p = fds; p < end; p++) {
#ifndef _WIN32
		/* TODO: some check for win32 */
		if (p->fd < 0 || p->fd >= FD_SETSIZE) {
			xp_seterrno (XP_EBADF);
			return -1;
		}
#endif

		if (p->events & XP_POLLIN) FD_SET (p->fd, &readset);
		if (p->events & XP_POLLOUT) FD_SET (p->fd, &writeset);
		if (p->events & XP_POLLPRI) FD_SET (p->fd, &exceptset);
		if (p->fd > max && 
		    (p->events & (XP_POLLIN|XP_POLLOUT|XP_POLLPRI))) max = p->fd;
	}

	n = (xp_ssize_t)select (max + 1, 
		&readset, &writeset, &exceptset, ((timeout >= 0)? &tv: XP_NULL));
	if (n == -1) {
#ifndef _WIN32
		xp_seterrno (xp_maperrno_wsa(WSAGetLastError()));
#endif
		return n;
	}
	if (n == 0) return n;

	for (p = fds, n = 0; p < end; p++) {
		p->revents = 0;

		if (FD_ISSET (p->fd, &readset)) p->revents |= XP_POLLIN;
		if (FD_ISSET (p->fd, &writeset)) p->revents |= XP_POLLOUT;
		if (FD_ISSET (p->fd, &exceptset)) p->revents |= XP_POLLPRI;

		if (p->revents != 0) n++;
	}

	return n;
}
#endif
